package whf.framework.workflow.assign;

import java.util.Collection;
import java.util.List;

import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.identity.assignment.ExpressionAssignmentException;
import org.jbpm.identity.assignment.TermTokenizer;
import org.jbpm.taskmgmt.def.AssignmentHandler;
import org.jbpm.taskmgmt.exe.Assignable;
import org.jbpm.taskmgmt.exe.SwimlaneInstance;

import whf.framework.entity.Entity;
import whf.framework.meta.MetaFactory;
import whf.framework.security.entity.User;
import whf.framework.util.BeanFactory;
import whf.framework.util.BeanUtils;
import whf.framework.util.Utils;
import whf.framework.workflow.engine.WorkflowConstants;

/**
 * @author king
 *
 */
@SuppressWarnings(value={"serial","unchecked"})
public class ExpressionAssignmentHandler implements AssignmentHandler {
	protected String expression;
	protected TermTokenizer tokenizer;
	protected ExecutionContext executionContext = null;
	Entity entity = null;
	
	public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception {
		this.tokenizer = new TermTokenizer(expression);
		this.executionContext = executionContext;
		
		List<String> terms = Utils.newArrayList();
		while(tokenizer.hasMoreTerms()) {
			terms.add(tokenizer.nextTerm());
		}
		Object previous = null;
		Object result = null;
		for(String term: terms) {
			result = this.resolveTerm(previous, term);
			if(result == null) {
				throw new ExpressionAssignmentException("Can not find any actors!");
			}
			previous = result;
		}
		//
		Collection<Object> actors = Utils.newHashSet();
		if(result instanceof User) {
			actors.add((User)result);
		} else if(result instanceof String) {
			actors.add(result);
		} else if(result instanceof Collection) {
			actors.addAll((Collection<User>)result);
		}
		if(actors.isEmpty()) {
			throw new ExpressionAssignmentException("Can not find any actors!");
		} else {
			if(actors.size() == 1) {
				for(Object actor: actors) {
					if(actor instanceof String) {
						assignable.setActorId((String)actor);
					} else if(actor instanceof User){
						assignable.setActorId(((User)actor).getUsername());
					} else {
						throw new ExpressionAssignmentException("Can not find any actors!");
					}
				}
			} else {
				String[] actorArray = new String[actors.size()];
				int c = 0;
				for(Object actor: actors) {
					if(actor instanceof String) {
						actorArray[c] = (String)actor;
					} else if(actor instanceof User){
						actorArray[c] = ((User)actor).getUsername();
					} else {
						throw new ExpressionAssignmentException("Can not find any actors!");
					}
					c ++;
				}
			}
		}
	}

	private Object resolveTerm(Object previous, final String term) throws Exception {
		int p = term.indexOf("(");
		String type = term.substring(0, p);
		String name = term.substring(p, term.length() - 1);
		if("org".equals(type)) {
			return this.getOrgs(previous, name);
		} else if("role".equals(type)){
			return this.getRoles(previous, name);
		} else if("hql".equals(type)) {
			return this.getUsersByHql(previous, name);
		} else if("user".equals(type)) {
			return this.getUsers(previous, name);
		} else if("swimlane".equals(type)) {
			return this.getSwimlaneActorId(previous, name);
		} else if("formValue".equals(type)) {
			return this.getFormValue(previous, name);
		} else {
			throw new ExpressionAssignmentException("can not resolve expression:" + term);
		}
	}
	
	private Object getOrgs(Object previous, String name) throws Exception {
		if(previous != null) {
			throw new ExpressionAssignmentException("org must be the first term!");
		}
		return AssignmentService.getAssignmentService().findDepts(name, name);
	}
	
	private Object getRoles(Object previous, String name) throws Exception {
		return AssignmentService.getAssignmentService().findUsersByRole((Collection<String>)previous, name);
	}
	
	private Object getUsersByHql(Object previous, String hql) throws Exception {
		if(previous != null) {
			throw new ExpressionAssignmentException("org must be the first term!");
		}
		return AssignmentService.getAssignmentService().findUsersByHql(null, null, hql);
	}
	
	private Object getUsers(Object previous, String username) throws Exception {
		return AssignmentService.getAssignmentService().findUsersByUsername(username);
	}

	private String getSwimlaneActorId(Object previous, String swimlaneName) {
		SwimlaneInstance swimlaneInstance = executionContext.getTaskMgmtInstance().getSwimlaneInstance(swimlaneName);
		if (swimlaneInstance == null) {
			throw new ExpressionAssignmentException("no swimlane instance '" + swimlaneName + "'");
		}
		return swimlaneInstance.getActorId();
	}
	
	private Object getFormValue(Object previous, String name) throws Exception {
		if(previous != null) {
			throw new ExpressionAssignmentException("formValue must be the first term!");
		}
		if(entity == null) {
			String entityType = (String)this.executionContext.getContextInstance().getVariable(WorkflowConstants.CONTEXT_VARIABLE_ENTITY_TYPE);
			String id = (String)this.executionContext.getContextInstance().getVariable(WorkflowConstants.CONTEXT_VARIABLE_ENTITY_ID);
			entity = BeanFactory.getService(MetaFactory.findByModelClass(Utils.getClassByClassName(entityType))).findByPrimaryKey(Long.parseLong(id));
		}
		return BeanUtils.getProperty(entity, name);
	}
}
